using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SmartLink.RedisRepository
{
    public class RedisManager
    {
        private static object _lock = new object();
        private static ConnectionMultiplexer connection;
        private static string connectionString = "";
        private static RedisManager instance;
        private static int dbindex = -1;
        private RedisManager()
        {
            int.TryParse(ConfigurationManager.AppSettings["RedisDB"], out dbindex);
            connection = Connection;
        }
        public static RedisManager GetInstance()
        {
            if (instance == null)
            {
                lock (_lock)
                {
                    if (instance == null)
                    {
                        instance = new RedisManager();
                    }
                }
            }
            return instance;
        }

        internal ConnectionMultiplexer Connection
        {
            get
            {
                if (connection == null || !connection.IsConnected)
                {
                    if (ConfigurationManager.ConnectionStrings["RedisServer"] != null)
                    {
                        connectionString = ConfigurationManager.ConnectionStrings["RedisServer"].ConnectionString;
                    }
                    //这个方式支持集群  直接ConnectionMultiplexer.Connect(configuration)的不支持2种redis集群 codis 和 Twemproxy
                    string[] EndPoint = connectionString.Split(',').Where(a => a.Contains(".") && a.Contains(":")).Select(a => a.Trim()).ToArray();
                    string Password = connectionString.Split(',').Where(a => a.Contains("=") && a.ToLower().Contains("password")).Select(a => a.Trim()).FirstOrDefault();
                    if (Password != null)
                    {
                        Password = Password.ToLower().Replace("password=", "");
                    }
                    var options = new ConfigurationOptions
                    {
                        Password = Password,
                        KeepAlive = 1,
                        Proxy = Proxy.Twemproxy,
                        AbortOnConnectFail = false
                    };
                    if (dbindex > 0)
                    {
                        options.Proxy = Proxy.None;
                    }
                    foreach (string item in EndPoint)
                    {
                        try
                        {
                            options.EndPoints.Add(item);
                        }
                        catch (Exception ex)
                        {
                            string err = ex.Message + ex.StackTrace;
                            SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "Connection", ex);
                        }
                    }
                    //var temp = ConnectionMultiplexer.Connect(connectionString);
                    connection = ConnectionMultiplexer.Connect(options);
                }
                return connection;
            }
        }

        protected IDatabase RedisDB
        {
            get { return Connection.GetDatabase(dbindex); }
        }

        public bool KeyDelete(string key)
        {
            try
            {
                return RedisDB.KeyDelete(key);
            }
            catch (Exception ex)
            {
                //SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "KeyDelete", ex);
                return false;
            }
        }


        #region string

        public string StringGet(string key)
        {
            try
            {
                return StringGetAsync(key, false);

            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "StringGet", ex);
                return string.Empty;
            }
        }

        public string StringGetAsync(string key, bool isasync = true)
        {
            var t = Task.Run<RedisValue>(() =>
            {
                return RedisDB.StringGetAsync(key);
            });

            if (isasync)
            {
                return t.Result;
            }
            else
            {
                t.Wait();
                return t.Result;
            }
        }

        public bool StringSet(string key, string val, TimeSpan? expiry = null)
        {
            try
            {
                StringSetAsync(key, val, expiry, false);
                return true;
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "StringSet", ex);
                return false;
            }
        }

        public bool StringSetAsync(string key, string val, TimeSpan? expiry = null, bool isasync = true)
        {
            var t = Task.Run(() =>
            {
                return RedisDB.StringSetAsync(key, val, expiry);
            });
            if (isasync)
            {
                return t.Result;
            }
            else
            {
                t.Wait();
                return t.Result;
            }
        }

        #endregion

        #region hashset
        public string HashGet(string key, string hashkey)
        {
            try
            {
                return HashGetAsync(key, hashkey, false);
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "HashGet", ex);
                return null;
            }
        }
        public string HashGetAsync(string key, string hashkey, bool isasync = true)
        {
            var t = Task.Run(() =>
            {
                return RedisDB.HashGetAsync(key, hashkey);
            });

            if (isasync)
            {
                return t.Result;
            }
            else
            {
                t.Wait();
                return t.Result;
            }
        }
        public Dictionary<string, string> HashGetAll(string key, bool isasync = false)
        {
            try
            {
                Dictionary<string, string> result = new Dictionary<string, string>();
                var t = Task.Run(() =>
                {
                    var data = RedisDB.HashGetAll(key);
                    foreach (var item in data)
                    {
                        result.Add(item.Name, item.Value);
                    }
                    return result;
                });
                if (isasync)
                {
                    return t.Result;
                }
                else
                {
                    t.Wait();
                    return t.Result;
                }
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "HashGetAll", ex);
                return null;
            }
        }

        public bool HashSet(string key, Dictionary<string, string> val, TimeSpan? expire = null)
        {
            return HashSetAsync(key, val, false, expire).Result;
        }

        public async Task<bool> HashSetAsync(string key, Dictionary<string, string> val, bool isasync = true, TimeSpan? expire = null)
        {
            var t = Task.Run<bool>(() =>
            {
                bool result = true;
                try
                {
                    List<HashEntry> list = new List<HashEntry>();
                    foreach (var item in val)
                    {
                        if (item.Value != null)
                        {
                            list.Add(new HashEntry(item.Key, item.Value));
                        }
                    }
                    RedisDB.HashSetAsync(key, list.ToArray());
                    if (expire != null)
                    {
                        RedisDB.KeyExpireAsync(key, expire);
                    }
                }
                catch (Exception ex)
                {
                    result = false;
                }
                return result;
            });
            if (isasync)
            {
                return t.Result;
            }
            else
            {
                t.Wait();
                return t.Result;
            }

            //try
            //{
            //    List<HashEntry> list = new List<HashEntry>();
            //    foreach (var item in val)
            //    {
            //        if (item.Value != null)
            //        {
            //            list.Add(new HashEntry(item.Key, item.Value));
            //        }
            //    }
            //    if (isasync)
            //    {
            //        RedisDB.HashSetAsync(key, list.ToArray());
            //        return true;
            //    }
            //    else
            //    {
            //        await RedisDB.HashSetAsync(key, list.ToArray());
            //        return true;
            //    }
            //}
            //catch (Exception ex)
            //{
            //    SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "HashSet", ex);
            //    return false;
            //}
        }
        public bool HashSet(string key, string hashkey, string val, TimeSpan? expiry = null)
        {
            try
            {
                var t = HashSetAsync(key, hashkey, val, false, expiry).Result;
                return true;
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "HashSet", ex);
                return false;
            }
        }

        public async Task<bool> HashSetAsync(string key, string hashkey, string val, bool isasync = true, TimeSpan? expiry = null)
        {
            //bool isok  = true;
            //try
            //{
            //    if (isasync)
            //    {
            //        RedisDB.HashSetAsync(key, hashkey, val);                 
            //    }
            //    else
            //    {
            //       var t= await RedisDB.HashSetAsync(key, hashkey, val);
            //    }
            //}
            //catch (Exception ex)
            //{
            //    SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "HashSet", ex);
            //    isok = false;
            //}
            //return isok;

            var t = Task.Run<bool>(() =>
            {
                RedisDB.HashSetAsync(key, hashkey, val);
                if (expiry != null)
                {
                    RedisDB.KeyExpireAsync(key, expiry);
                }
                return true;
            });

            if (isasync)
            {
                return t.Result;
            }
            else
            {
                t.Wait();
                return t.Result;
            }

        }

        public bool HashDelete(string key, string hashkey)
        {
            try
            {
                HashDeleteAsync(key, hashkey, false);
                return true;
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "HashDelete", ex);
                return false;
            }
        }

        public bool HashDeleteAsync(string key, string hashkey, bool isasync = true)
        {
            var t = Task.Run(() =>
            {
                return RedisDB.HashDeleteAsync(key, hashkey);
            });
            if (isasync)
            {
                return t.Result;
            }
            else
            {
                t.Wait();
                return t.Result;
            }
        }

        #endregion

        #region queue
        public bool QueueSet(string key, string value)
        {
            try
            {
                return QueueSetAsync(key, value);
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "QueueSet", ex);
                return false;
            }
        }

        private bool QueueSetAsync(string key, string value)
        {
            var t = Task.Run(() =>
            {
                return RedisDB.ListLeftPushAsync(key, value);

            });
            t.Wait();
            return t.Result > 0;
        }

        public string QueueGet(string key)
        {
            try
            {
                return QueueGetAsync(key);
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "QueueGet", ex);
                return string.Empty;
            }
        }

        public string QueueGetAsync(string key)
        {
            //var t = Task.Run(() =>
            //{
            //    return RedisDB.ListRightPopAsync(key);
            //});
            //t.Wait();
            //return t.Result;
            var aPending = RedisDB.ListRightPopAsync(key);
            var a = RedisDB.Wait(aPending);
            return a;
        }

        #endregion

        #region sortset
        public bool SortSetAsync(string key, string member, double score)
        {
            try
            {
                var t = RedisDB.SortedSetAddAsync(key, member, score).Result;
                return true;
                //Task.Run(() => {
                //    RedisDB.SortedSetAddAsync(key, member, score);
                //});
                //return true;
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "SortSetAsync", ex);
                return false;
            }
        }

        public bool SortedSetRemoveAsync(string key, string member)
        {
            try
            {
                return RedisDB.SortedSetRemoveAsync(key, member).Result;
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "SortedSetRemoveAsync", ex);
                return false;
            }
        }

        public string[] SortedSetRangeByScore(string key, double star, double stop)
        {
            try
            {
                var t = RedisDB.SortedSetRangeByScoreAsync(key, star, stop);
                var tv = RedisDB.Wait(t);
                return tv.ToStringArray();
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "SortedSetRemoveAsync", ex);
                return null;
            }
        }
        #endregion

        #region set
        public bool SetAsync(string key, string value)
        {
            try
            {
                var t = RedisDB.SetAddAsync(key, value).Result;
                return true;
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "SetAsync", ex);
                return false;
            }
        }

        public bool SetAsync(string key, string[] values)
        {
            try
            {
                var members = Array.ConvertAll(values, value => (RedisValue)value);
                var t = RedisDB.SetAddAsync(key, members).Result;
                return true;
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "SetAsync", ex);
                return false;
            }
        }

        public string[] SetMembers(string key)
        {
            try
            {
                return RedisDB.SetMembers(key).Select(a => a.ToString()).ToArray();
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "SetMembers", ex);
                return null;
            }
        }

        public bool SetRemoveAsync(string key, string value)
        {
            try
            {
                return RedisDB.SetRemoveAsync(key, value).Result;
            }
            catch (Exception ex)
            {
                SmartLink.Infrastructure.Logger.AppLogger.Error("redis_" + "SetRemoveAsync", ex);
                return false;
            }
        }
        #endregion
    }
}
